#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include "util.hpp"

//const std::string path = "data/input";
const std::string path = "/home/shr/MyGitee/boost-search-engine/boost-searcher/data/input";
const std::string output = "/home/shr/MyGitee/boost-search-engine/boost-searcher/data/raw_htmls/raw.bin";
//const std::string output = "data/raw_htmls/raw.bin";

struct DocInfo
{
    std::string _title;
    std::string _content;
    std::string _url;
};

enum {
    EnumFileErr = 1,
    ParseHtmlErr,
    SaveHtmlErr
};

bool EnumFile(const std::string& path, std::vector<std::string>* files_list);
bool ParseHtml(const std::vector<std::string>& files_list, std::vector<DocInfo>* results);
bool SaveHtml(const std::vector<DocInfo>& results, const std::string& output);

std::string getBoostVersion()
{
    namespace fs = boost::filesystem;
    fs::path dir_path("/home/shr/MyGitee/boost-search-engine/boost-searcher/data/boost_d");
    //路径不存在，直接返回
    if(!fs::exists(dir_path)) {
        std::cerr << "path not exists" << std::endl;
        return "";
    }
    std::string boostName;
    for (const auto &entry : fs::directory_iterator(dir_path)) {
        if(fs::is_directory(entry)) {
            boostName = entry.path().string();
            break;
        }
    }
    size_t pos = boostName.rfind('/');
    if(pos == std::string::npos) { std::cerr << "get boost name err" << std::endl; }
    boostName = boostName.substr(pos + 1);
    std::cerr << "boost name: " << boostName << std::endl;
    pos = boostName.find('_');
    if(pos == std::string::npos) { std::cerr << "get boost version err" << std::endl; }
    std::cerr << "get boost version: " << boostName.substr(pos + 1) << std::endl;
    return boostName.substr(pos + 1);
}

const std::string boostVersion = getBoostVersion();

int main()
{
    std::vector<std::string> files_list;
    //遍历path路径下的所有文件，将文件名+路径填入到files_list，方便后续进行文件操作
    if(!EnumFile(path, &files_list)) {
        std::cerr << "EnumFile err" << std::endl;
        exit(EnumFileErr);
    }
    //解析files_list中的所有文件，填入到results中
    std::vector<DocInfo> results;
    if(!ParseHtml(files_list, &results)) {
        std::cerr << "ParseHtml err" << std::endl;
        exit(ParseHtmlErr);
    }
    
    //将results中的信息保存到output中，以\3分割
    if(!SaveHtml(results, output)) {
        std::cerr << "SaveHtml err" << std::endl;
        exit(SaveHtmlErr);
    }

    return 0;
}

bool EnumFile(const std::string& path, std::vector<std::string>* files_list)
{
    namespace fs = boost::filesystem;
    fs::path root_path(path);
    //路径不存在，直接返回
    if(!fs::exists(root_path)) {
        std::cerr << "path not exists" << std::endl;
        return false;
    }
    //空的recursive_directory_iterator对象标识了遍历末尾
    //类似end迭代器
    fs::recursive_directory_iterator end;
    //递归遍历文件系统
    for(fs::recursive_directory_iterator iter(root_path); iter != end; ++iter)
    {
        //判断是否是常规文件
        if(!fs::is_regular_file(*iter)) { continue; }
        //判断文件后缀是否为.html
        if(iter->path().extension() != ".html") { continue; }
        //保存到文件列表
        files_list->push_back(iter->path().string());
    }
    return true;
}

static bool ParseTitle(const std::string& file, std::string* title)
{
    int begin = file.find("<title>");
    if(begin == std::string::npos) {
        return false;
    }
    int end = file.find("</title>");
    if(end == std::string::npos) {
        return false;
    }
    begin += std::string("<title>").size();
    if(begin >= end) {
        return false;
    }
    *title = file.substr(begin, end - begin);
    return true;
}
//parseContent时，是对整个文档进行parse的，此时会将title纳入content内
static bool ParseContent(const std::string& file, std::string* content)
{
    //基于一个简易的状态机
    enum status {
        LABLE,
        CONTENT
    };
    //一个html文档的开头一定是LABLE
    enum status s = LABLE;
    for(char ch : file)
    {
        if(s == LABLE) {
            if(ch == '>') { s = CONTENT; }
        }
        else if(s == CONTENT)
        {
            if(ch == '<') { s = LABLE; }
            else {
                //不要'\n'
                if(ch == '\n') { ch = ' '; }
                *content += ch;
            }
        }
        else { }
    }
    return true;
}

static bool ParseUrl(const std::string& file_path, std::string* url)
{
    std::string url_head = "https://www.boost.org/doc/libs/";
    url_head += boostVersion;
    url_head += "/doc/html";
    std::string url_tail = file_path.substr(path.size());
    *url = url_head + url_tail;
    return true;
}

//for debug
// void DebugParse(const DocInfo& doc)
// {
//     std::cout << "title: " << doc._title << std::endl;
//     std::cout << "content: " << doc._content << std::endl;
//     std::cout << "url: " << doc._url << std::endl;
// }

bool ParseHtml(const std::vector<std::string>& files_list, std::vector<DocInfo>* results)
{
    for(const std::string& file_path : files_list)
    {
        std::string file;
        DocInfo result;
        //1.读取文件
        if(!ns_util::FileUtil::ReadFile(file_path, &file)) {
            continue;
        }
        //2.提取各个文件的title
        if(!ParseTitle(file, &result._title)) {
            continue;
        }
        //3.提取各个文件的content
        if(!ParseContent(file, &result._content)) {
            continue;
        }
        //4.构造各个文件的url
        if(!ParseUrl(file_path, &result._url)) {
            continue;
        }
        //move临时DocInfo对象，避免拷贝
        results->push_back(std::move(result));
        
        // for debug
        // DebugParse(result);
        // break;
    }
    return true;
}

bool SaveHtml(const std::vector<DocInfo>& results, const std::string& output)
{
    //以二进制方式写入，避免程序对数据进行修改
    std::ofstream out(output, std::ios::out | std::ios::binary);
    if(!out.is_open()) {
        std::cerr << "open file " << output << " err" << std::endl;
        return false;
    }
    //title\3content\3url\n
    for(const DocInfo& result : results)
    {
        std::string out_buff;
        out_buff = result._title;
        out_buff += '\3';
        out_buff += result._content;
        out_buff += '\3';
        out_buff += result._url;
        out_buff += '\n';
        out << out_buff;
    }
    return true;
}
